'use strict'

/*  探索bebel遍历过程中使用的path
    "Path 是表示两个节点之间连接的对象。" --jamiebuilds/babel-handbook
    AST树中Node节点是父子关系，那么Path表示与什么节点的什么连接关系呢？下面通过对Path对象的研究来一窥究竟
*/

const parser = require('@babel/parser');
const traverse = require('D:\\WorkSpace\\js_obscure_reducation\\node_modules\\@babel/traverse').default;
const types = require('@babel/types');
const generator = require('D:\\WorkSpace\\js_obscure_reducation\\node_modules\\@babel/generator').default;

let code = `
    let a=1, b=2;
    function sum(a, b){
        return a+b
    }
    sum(a,b);
`

let ast = parser.parse(code)

let idf_path_get, idf_path_tra;

let myvisitor = {
  FunctionDeclaration:{
      enter: function(path){
        // console.log(Object.keys(path));
        // console.log(Object.getOwnPropertyDescriptors(path));
        // console.log(path.context.queue)
        
        // for(let attr in path){
        //     console.log(attr+": " + path[attr]);
        // }
        debugger;
        idf_path_get = path.get('id');
        console.log('1')
        console.log(idf_path_get.parent.type, idf_path_get.node.name);
      },
  },

  // Identifier: {
  //   enter: function(path){
  //     if(path.parent.type == 'FunctionDeclaration' & path.node.name == 'sum'){
  //       idf_path_tra = path;
  //       console.log('2')
  //       console.log(idf_path_tra.parent.type, idf_path_tra.node.name);
  //     }
  //   },
  // },
}

let myvisitorx = {
  Identifier: {
    enter: function(path){
      if(path.parent.type == 'FunctionDeclaration' & path.node.name == 'sum'){
        idf_path_tra = path;
        console.log('3')
        console.log(idf_path_tra.parent.type, idf_path_tra.node.name);
      }
    },
  },
}

// FunctionDeclaration, Identifier两个opts位于同一visitor中，在一次遍历过程中
// traverse(ast, myvisitor);
// console.log(idf_path_get === idf_path_tra);    // true
// console.log(idf_path_get.node === idf_path_tra.node);    // true

// FunctionDeclaration, Identifier两个opts位于不同visitor中（注释掉myvisitor.Identifier），各自位于不用遍历中
// 由打印结果可知，traverse在遍历过程中，内部肯定依据ast生成了一幅path，并且是在第一次遍历时就生成好（创建各个path）
// 只不过这幅path不能通过node直接获得

// 经debug调试，发现在遍历过程中会维护一个_cache，_cache:{path, scope},通过NodePath.get({hub,parentPath,parent,container,listKey,key})方式查找
// 可以理解为Babel为了优化过程，虽然规避不了再次遍历，但是可以给每次遍历过程瘦身
traverse(ast, myvisitor);
console.log(idf_path_get === idf_path_tra);    // true
console.log('.....................................')
traverse(ast.program, myvisitorx);
console.log(idf_path_get === idf_path_tra);    // true
console.log(idf_path_get.node === idf_path_tra.node);    // true

/* 打印如下
>> console.log(Object.keys(path)):
[
  'contexts',  'state',
  'opts',      '_traverseFlags',
  'skipKeys',  'parentPath',
  'container', 'listKey',
  'key',       'node',
  'type',      'parent',
  'hub',       'data',
  'context',   'scope'
]

>> console.log(Object.getOwnPropertyDescriptors(path)):
{
  contexts: {
    value: [ [TraversalContext] ],
    writable: true,
    enumerable: true,
    configurable: true
  },
  state: {
    value: undefined,
    writable: true,
    enumerable: true,
    configurable: true
  },
  opts: {
    value: { FunctionDeclaration: [Object], _exploded: true, _verified: true },
    writable: true,
    enumerable: true,
    configurable: true
  },
  _traverseFlags: { value: 0, writable: true, enumerable: true, configurable: true },
  skipKeys: { value: null, writable: true, enumerable: true, configurable: true },
  parentPath: {
    value: NodePath {
      contexts: [Array],
      state: undefined,
      opts: [Object],
      _traverseFlags: 0,
      skipKeys: null,
      parentPath: null,
      container: [Node],
      listKey: undefined,
      key: 'program',
      node: [Node],
      type: 'Program',
      parent: [Node],
      hub: undefined,
      data: null,
      context: [TraversalContext],
      scope: [Scope]
    },
    writable: true,
    enumerable: true,
    configurable: true
  },
  container: {
    value: [ [Node], [Node], [Node] ],
    writable: true,
    enumerable: true,
    configurable: true
  },
  listKey: {
    value: 'body',
    writable: true,
    enumerable: true,
    configurable: true
  },
  key: { value: 1, writable: true, enumerable: true, configurable: true },
  node: {
    value: Node {
      type: 'FunctionDeclaration',
      start: 23,
      end: 67,
      loc: [SourceLocation],
      id: [Node],
      generator: false,
      async: false,
      params: [Array],
      body: [Node]
    },
    writable: true,
    enumerable: true,
    configurable: true
  },
  type: {
    value: 'FunctionDeclaration',
    writable: true,
    enumerable: true,
    configurable: true
  },
  parent: {
    value: Node {
      type: 'Program',
      start: 0,
      end: 82,
      loc: [SourceLocation],
      sourceType: 'script',
      interpreter: null,
      body: [Array],
      directives: []
    },
    writable: true,
    enumerable: true,
    configurable: true
  },
  hub: {
    value: undefined,
    writable: true,
    enumerable: true,
    configurable: true
  },
  data: { value: null, writable: true, enumerable: true, configurable: true },
  context: {
    value: TraversalContext {
      queue: [Array],
      priorityQueue: [],
      parentPath: [NodePath],
      scope: [Scope],
      state: undefined,
      opts: [Object]
    },
    writable: true,
    enumerable: true,
    configurable: true
  },
  scope: {
    value: Scope {
      uid: 1,
      path: [NodePath],
      block: [Node],
      labels: Map(0) {},
      inited: true,
      bindings: [Object: null prototype],
      references: [Object: null prototype] {},
      globals: [Object: null prototype] {},
      uids: [Object: null prototype] {},
      data: [Object: null prototype] {},
      crawling: undefined
    },
    writable: true,
    enumerable: true,
    configurable: true
  }
}
*/
